﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WJP.Utils;

namespace WJP.Queue.Socketing.Framing
{
    /// <summary>
    /// 长度前缀消息帧组装类
    /// </summary>
    public class LengthPrefixMessageFramer : IMessageFramer
    {
        public const int HeaderLength = sizeof(int);
        private Action<ArraySegment<byte>> _receivedHandler;

        private byte[] _messageBuffer;
        private int _bufferIndex = 0;
        private int _headerBytes = 0;
        private int _packageLength = 0;

        /// <summary>
        /// 组装帧数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public IEnumerable<ArraySegment<byte>> FrameData(ArraySegment<byte> data)
        {
            var length = data.Count;
            yield return new ArraySegment<byte>(new[]
            {
                (byte)length, (byte)(length >> 8), (byte)(length >> 16), (byte)(length >> 24)
            });
            yield return data;
        }

        /// <summary>
        /// 解析帧数据，当数据完全到达后触发回调方法
        /// </summary>
        /// <param name="data"></param>
        public void UnFrameData(IEnumerable<ArraySegment<byte>> data)
        {
            Ensure.NotNull(data, "data");

            foreach(var buffer in data)
            {
                Parse(buffer);
            }
        }

        /// <summary>
        /// 解析帧数据，当数据完全到达后触发回调方法
        /// </summary>
        /// <param name="data"></param>
        public void UnFrameData(ArraySegment<byte> data)
        {
            Parse(data);
        }

        /// <summary>
        /// 注册消息完全到达后回调方法
        /// </summary>
        /// <param name="handler"></param>
        public void RegisterMessageArrivedCallback(Action<ArraySegment<byte>> handler)
        {
            _receivedHandler = handler ?? throw new ArgumentNullException("handler");
        }

        /// <summary>
        /// 基于长度前缀帧解析流chunking。
        /// <para>调用是可重复进入的，内部保持状态。一旦完整的消息到达，就会引发回调</para>
        /// <para>回调方法是使用<see cref="RegisterMessageArrivedCallback(Action{ArraySegment{byte}})"/>方法注册的</para>
        /// </summary>
        /// <param name="bytes">要追加的数据的字节数组</param>
        private void Parse(ArraySegment<byte> bytes)
        {
            byte[] data = bytes.Array;
            for(int i = bytes.Offset; i < bytes.Offset + bytes.Count; i++)
            {
                if(_headerBytes < HeaderLength)
                {
                    _packageLength |= (data[i] << (_headerBytes * 8)); //低位优先顺序
                    ++_headerBytes;
                    if(_headerBytes == HeaderLength)
                    {
                        if(_packageLength <= 0)
                        {
                            throw new Exception("包长度 ({0} 超出范围。".FormatArgs(_packageLength));
                        }
                        _messageBuffer = new byte[_packageLength];
                    }
                }
                else
                {
                    int copyCnt = Math.Min(bytes.Count + bytes.Offset - i, _packageLength - _bufferIndex);
                    try
                    {
                        Buffer.BlockCopy(bytes.Array, i, _messageBuffer, _bufferIndex, copyCnt);
                    }
                    catch (Exception ex)
                    {
                        Logging.LogManager.Logger.Error("解析消息缓冲区失败, _headerLength: {0}, _packageLength: {1}, _bufferIndex: {2}, copyCnt: {3}, _messageBuffer is null: {4}"
                            .FormatArgs(_headerBytes, _packageLength, _bufferIndex, copyCnt, _messageBuffer == null), ex);
                        throw;
                    }

                    _bufferIndex += copyCnt;
                    i += copyCnt - 1;

                    if(_bufferIndex == _packageLength)
                    {
                        if(_receivedHandler != null)
                        {
                            try
                            {
                                _receivedHandler(new ArraySegment<byte>(_messageBuffer, 0, _bufferIndex));
                            }
                            catch (Exception ex)
                            {
                                Logging.LogManager.Logger.Error("处理接收到的消息失败。", ex);
                            }
                        }
                        _messageBuffer = null;
                        _headerBytes = 0;
                        _packageLength = 0;
                        _bufferIndex = 0;
                    }
                }
            }
        }
    }
}
